Skip to content

Ensure stable routing for x-modulus-hash exchange#15849

Merged
ansd merged 3 commits intomainfrom
modulus-hash-stable-routing
Mar 27, 2026
Merged

Ensure stable routing for x-modulus-hash exchange#15849
ansd merged 3 commits intomainfrom
modulus-hash-stable-routing

Conversation

@ansd
Copy link
Copy Markdown
Member

@ansd ansd commented Mar 26, 2026

What?

This PR makes routing stable for the x-modulus-hash exchange: If the same destination queues stay bound to the exchange (i.e. do not bind or unbind queues after the "initial setup"), messages with the same domain entity (routing key) will always end up in the same destination queue, even across node restarts.

Move the x-modulus-hash exchange type from rabbitmq_sharding to rabbit since this exchange type is useful even without the sharding plugin.

How?

With Mnesia this was guaranteed due to order_set tables. Khepri introduced a regression since it uses a bag ETS projection table.

This PR simply sorts the destinations before picking the Nth destination.

Why?

Uses cases where message order matters are common. This PR allows to for example bind N quorum queues to an x-modulus-hash exchange instance (the binding keys do not matter) and use the Single Active Consumer (SAC) feature on each quorum queue. This will provide

  • message ordering thanks to stable routing and SAC
  • concurrent consumption: N app instances can process messages in parallel
  • fault tolerance since the broker will deliver messages to another consumer when the active consumer crashes

Using the consistent hash exchange is an alternative, but unnecessarily complex for this use case.

Yet another alternative for this use case is using the murmur3 exchange type (#8319).

Docs:

rabbitmq/rabbitmq-website#2494

@ansd ansd requested a review from kjnilsson March 26, 2026 20:26
@ansd ansd added this to the 4.3.0 milestone Mar 26, 2026
@ansd ansd force-pushed the modulus-hash-stable-routing branch from 6900588 to f4ffd85 Compare March 26, 2026 20:40
ansd added 3 commits March 27, 2026 15:18
 ## What?

This commit makes routing stable for the `x-modulus-hash` exchange:
If the same destination queues stay bound to the exchange (i.e. do not
bind or unbind queues after the "initial setup"), messages with the same
domain entity (routing key) will always end up in the same destination queue,
even across node restarts.

 ## How?

With Mnesia this was guaranteed due to order_set tables. Khepri
introduced a regression since it uses a bag ETS projection table.

This commit simply sorts the destinations before picking the Nth
destination.

 ## Why?

Uses cases where message order matters are common. This commit allows to
for example bind N quorum queues to an `x-modulus-hash` exchange
instance (the binding key doesn't matter) and use the Single Active
Consumer (SAC) feature on each quorum queue. This will provide
* message ordering thanks to stable routing and SAC
* concurrent consumption: N app instances can process messages in parallel
* fault tolerance since the broker will deliver messages to another consumer
  when the active consumer crashes

Using the consistent hash exchange is an alternative, but unnecessarily complex
for this use case.

Yet another alternative for this use case is using the murmur3 exchange
type (#8319).
Move the x-modulus-hash exchange type from rabbitmq_sharding to rabbit
since this exchange type is useful even without the sharding plugin.
This commit adds a test case to explicitly verify that the
`x-modulus-hash` exchange supports weighted routing.

Because the exchange uses `lists:sort/1` instead of `lists:usort/1`
when fetching the bound destinations, a user can bind the exact same
queue multiple times to the exchange (using different dummy binding
keys) to increase its weight. If a queue is bound N times, it will
appear N times in the destination list, giving it a proportionally
higher chance of receiving messages.
@ansd ansd force-pushed the modulus-hash-stable-routing branch from f4ffd85 to d7b3d19 Compare March 27, 2026 14:58
@mergify mergify Bot added the make label Mar 27, 2026
@ansd ansd marked this pull request as ready for review March 27, 2026 15:23
ansd added a commit to rabbitmq/rabbitmq-website that referenced this pull request Mar 27, 2026
This commit adds documentation for the built-in `x-modulus-hash` exchange.
It adds a dedicated page explaining its core features: stable routing,
weighted routing, and how it compares to the consistent hash exchange.

It also adds a bullet point to the message ordering section in `queues.md`
to highlight the common use case of using `x-modulus-hash` with Single
Active Consumer (SAC) to process messages concurrently while preserving
order for each domain entity.

Docs for rabbitmq/rabbitmq-server#15849
ansd added a commit to rabbitmq/rabbitmq-website that referenced this pull request Mar 27, 2026
This commit adds documentation for the built-in `x-modulus-hash` exchange.
It adds a dedicated page explaining its core features: stable routing,
weighted routing, and how it compares to the consistent hash exchange.

It also adds a bullet point to the message ordering section in `queues.md`
to highlight the common use case of using `x-modulus-hash` with Single
Active Consumer (SAC) to process messages concurrently while preserving
order for each domain entity.

Docs for rabbitmq/rabbitmq-server#15849
@ansd ansd merged commit 864754e into main Mar 27, 2026
184 checks passed
@ansd ansd deleted the modulus-hash-stable-routing branch March 27, 2026 16:54
ansd added a commit that referenced this pull request Mar 27, 2026
Ensure stable routing for `x-modulus-hash` exchange (backport #15849)
@michaelklishin michaelklishin modified the milestones: 4.3.0, 4.4.0 Mar 27, 2026
michaelklishin added a commit that referenced this pull request Mar 27, 2026
michaelklishin added a commit that referenced this pull request Mar 27, 2026
michaelklishin added a commit that referenced this pull request Mar 27, 2026
mergify Bot pushed a commit that referenced this pull request Mar 27, 2026
[ci skip]

(cherry picked from commit b8777fc)
michaelklishin added a commit that referenced this pull request Mar 27, 2026
acogoluegnes added a commit to rabbitmq/rabbitmq-java-client that referenced this pull request Mar 30, 2026
acogoluegnes added a commit to rabbitmq/rabbitmq-java-client that referenced this pull request Mar 31, 2026
RabbitMQ 4.3+.

References rabbitmq/rabbitmq-server#15849
References #1918

(cherry picked from commit 9c41e2c)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants